﻿using System;
using System.IO;
using System.Collections.Generic;
using System.Reflection;
using System.Data;
using System.Data.Linq.Mapping;
using Microsoft.Data.Sqlite;

using Fasterflect;

using KorbeRTR.Utility.HslWapper;

namespace KorbeRTR.Utility
{
    public class AuxLib
    {
        public static void BackupDatabase(DateTime time)
        {
            var dir = Path.Combine("Database", "Backup");
            if (!Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            string BackupConnectString = "Data Source=Database/Backup/" + time.ToString("yyyyMMdd-HHmmss") + "-RecordDB.db";
            using (var BackupConn = new SqliteConnection(BackupConnectString))
            {
                BackupConn.Open();

                using (var conn = new SqliteConnection(Utility.OperationRecordDBHelper.ConnectString))
                {
                    conn.Open();

                    conn.BackupDatabase(BackupConn);
                }
            }

            BackupConnectString = "Data Source=Database/Backup/" + time.ToString("yyyyMMdd-HHmmss") + "-RecipesDB.db";
            using (var BackupConn = new SqliteConnection(BackupConnectString))
            {
                BackupConn.Open();

                using (var conn = new SqliteConnection(Utility.RecipesDBHelper.ConnectString))
                {
                    conn.Open();

                    conn.BackupDatabase(BackupConn);
                }
            }

            //Models.RecipeView.DeleteBeforeMonth(DateTime.Now, 1);
            //Models.LoadingView.DeleteBeforeMonth(DateTime.Now, 1);

            Models.AlarmRecordView.DeleteBeforeMonth(DateTime.Now, 1);
            Models.CurrentRecordView.DeleteBeforeMonth(DateTime.Now, 1);
            Models.TemperatureRecord.DeleteBeforeMonth(DateTime.Now, 1);
            Models.OperationRecord.DeleteBeforeMonth(DateTime.Now, 1);

            Utility.OperationRecordDBHelper.ExecuteNonQuery("VACUUM;");
        }

        public static void Swap<T>(ref T a, ref T b)
        {
            var tmp = a;
            a = b;
            b = tmp;
        }

        public static T Min<T>(T a, T b) where T: IComparable<T>
        {
            return (a.CompareTo(b) < 0) ? a : b;
        }

        public static T Max<T>(T a, T b) where T : IComparable<T>
        {
            return (a.CompareTo(b) > 0) ? a : b;
        }

        public static T Bound<T>(T min, T x, T max) where T : IComparable<T>
        {
            return Max(min, Min(x, max));
        }

        public static UInt16 BCD2UInt16(UInt16 bcd)
        {
            int bin = (bcd & 0x000F);
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 100;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 1000;
            return (UInt16)bin;
        }

        public static UInt16 UInt162BCD(UInt16 bin)
        {
            int bcd = bin % 10;
            bin /= 10;
            bcd += (bin % 10) << 4;
            bin /= 10;
            bcd += (bin % 10) << 8;
            bin /= 10;
            bcd += (bin % 10) << 12;
            return (UInt16)bcd;
        }
    }

    public static class AuxLibExtersion
    {
        public static double ToDouble(this string s)
        {
            try
            {
                return string.IsNullOrWhiteSpace(s) ? 0 : double.Parse(s);
            }
            catch
            {
                return 0;
            }
        }
        public static double ToInt(this string s)
        {
            try
            {
                return string.IsNullOrWhiteSpace(s) ? 0 : int.Parse(s);
            }
            catch
            {
                return 0;
            }
        }

        public static string ToSqlTime(this DateTime t)
        {
            return t.ToString("yyyy-MM-dd HH:mm:ss.fff");
        }

        public static T ToModel<T>(this DataRow dr)
        {
            var type = typeof(T);
            var m = Activator.CreateInstance<T>();

            foreach(var prop in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
            {
                var attr = prop.GetCustomAttribute<ColumnAttribute>();
                if (attr == null) continue;

                string column = string.IsNullOrWhiteSpace(attr.Name) ? prop.Name : attr.Name;
                if (dr[column] is DBNull)
                {
                    prop.SetValue(m, null);
                }
                else if (prop.PropertyType == dr[column].GetType())
                {
                    prop.SetValue(m, dr[column]);
                }
                else
                {
                    prop.SetValue(m, Convert.ChangeType(dr[column], prop.PropertyType));
                }
                
            }

            return m;
        }

        public static T ToModel<T>(this DataRow dr, T m)
        {
            var type = typeof(T);

            foreach (var prop in type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance))
            {
                var attr = prop.GetCustomAttribute<ColumnAttribute>();
                if (attr == null) continue;

                string column = string.IsNullOrWhiteSpace(attr.Name) ? prop.Name : attr.Name;
                if (dr[column] is DBNull)
                {
                    prop.SetValue(m, null);
                }
                else if (prop.PropertyType == dr[column].GetType())
                {
                    prop.SetValue(m, dr[column]);
                }
                else
                {
                    prop.SetValue(m, Convert.ChangeType(dr[column], prop.PropertyType));
                }

            }

            return m;
        }

        public static T ToModel<T>(this DataRow dr, T m, PropertyInfo[] properties)
        {
            foreach (var prop in properties)
            {
                var attr = prop.GetCustomAttribute<ColumnAttribute>();
                if (attr == null) continue;

                string column = string.IsNullOrWhiteSpace(attr.Name) ? prop.Name : attr.Name;
                var obj = dr[column];
                if (obj is DBNull)
                {
                    prop.SetValue(m, null);
                }
                else if (prop.PropertyType == obj.GetType())
                {
                    prop.SetValue(m, obj);
                }
                else
                {
                    prop.SetValue(m, Convert.ChangeType(obj, prop.PropertyType));
                }

            }

            return m;
        }

        public static List<T> ToModel<T>(this DataTable dt)
        {
            var list = new List<T>(dt.Rows.Count);

            var type = typeof(T);
            var properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance);

            var map = new Dictionary<string, Tuple<string, Type>>();
            foreach (var prop in properties)
            {
                var attr = prop.GetCustomAttribute<ColumnAttribute>();
                if (attr == null) continue;

                string column = string.IsNullOrWhiteSpace(attr.Name) ? prop.Name : attr.Name;
                map[prop.Name] = new Tuple<string, Type>(column, prop.PropertyType);
            }

            foreach (DataRow dr in dt.Rows)
            {
                var m = type.CreateInstance();

                foreach (var prop in map.Keys)
                {
                    string column = map[prop].Item1;
                    Type PropertyType = map[prop].Item2;

                    var obj = dr[column];
                    if (obj is DBNull)
                    {
                        m.SetPropertyValue(prop, null);
                    }
                    else if (PropertyType == obj.GetType())
                    {
                        m.SetPropertyValue(prop, obj);
                    }
                    else
                    {
                        m.SetPropertyValue(prop, Convert.ChangeType(obj, PropertyType));
                    }
                }

                list.Add((T)m);
            }

            return list;
        }
    }

    /// <summary>
    /// BCD整数
    /// </summary>
    public class BCDF0Uint16 : HslWapper.Variable<double, UInt16>
    {
        #region 单独变量
        public BCDF0Uint16(string Area, int Addr, int? SubAddr = null) : base(Area, Addr, SubAddr)
        {
        }

        public BCDF0Uint16(string Area, int Addr, string Comment) : base(Area, Addr, null, Comment)
        {
        }

        public BCDF0Uint16(string Area, int Addr, int? SubAddr, string Comment) : base(Area, Addr, SubAddr, Comment)
        {
        }
        #endregion

        #region 属于变量组
        public BCDF0Uint16(string Area, int Addr, VariableGroupAttribute Group) : base(Area, Addr, Group)
        {
        }
        public BCDF0Uint16(string Area, int Addr, int SubAddr, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Group)
        {
        }
        public BCDF0Uint16(string Area, int Addr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, Comment, Group)
        {
        }
        public BCDF0Uint16(string Area, int Addr, int SubAddr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Comment, Group)
        {
        }
        #endregion

        /// <summary>
        /// 视在值
        /// </summary>
        public override double Value
        {
            get => BCD2Double(RawValue);
            set => RawValue = Double2BCD(value);
        }

        public override double LastValue
        {
            get
            {
                return BCD2Double(LastRawValue);
            }
        }

        private double BCD2Double(UInt16 bcd)
        {
            int bin = (bcd & 0x000F);
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 100;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 1000;
            return bin;
        }

        private UInt16 Double2BCD(double x)
        {
            UInt16 bin = Convert.ToUInt16(x);
            int bcd = bin % 10;
            bin /= 10;
            bcd += (bin % 10) << 4;
            bin /= 10;
            bcd += (bin % 10) << 8;
            bin /= 10;
            bcd += (bin % 10) << 12;
            return (UInt16)bcd;
        }

        public override string ValueString()
        {
            return Value.ToString();
        }
    }

    /// <summary>
    /// BCD一位小数
    /// </summary>
    public class BCDF1Uint16 : HslWapper.Variable<double, UInt16>
    {
        #region 单独变量
        public BCDF1Uint16(string Area, int Addr, int? SubAddr = null) : base(Area, Addr, SubAddr)
        {
        }

        public BCDF1Uint16(string Area, int Addr, string Comment) : base(Area, Addr, null, Comment)
        {
        }

        public BCDF1Uint16(string Area, int Addr, int? SubAddr, string Comment) : base(Area, Addr, SubAddr, Comment)
        {
        }
        #endregion

        #region 属于变量组
        public BCDF1Uint16(string Area, int Addr, VariableGroupAttribute Group) : base(Area, Addr, Group)
        {
        }
        public BCDF1Uint16(string Area, int Addr, int SubAddr, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Group)
        {
        }
        public BCDF1Uint16(string Area, int Addr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, Comment, Group)
        {
        }
        public BCDF1Uint16(string Area, int Addr, int SubAddr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Comment, Group)
        {
        }
        #endregion

        /// <summary>
        /// 视在值
        /// </summary>
        public override double Value
        {
            get => BCD2Double(RawValue) / 10;
            set => RawValue = Double2BCD(value * 10);
        }

        public override double LastValue
        {
            get
            {
                return BCD2Double(LastRawValue) / 10;
            }
        }

        private double BCD2Double(UInt16 bcd)
        {
            int bin = (bcd & 0x000F);
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 100;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 1000;
            return bin;
        }

        private UInt16 Double2BCD(double x)
        {
            UInt16 bin = Convert.ToUInt16(x);
            int bcd = bin % 10;
            bin /= 10;
            bcd += (bin % 10) << 4;
            bin /= 10;
            bcd += (bin % 10) << 8;
            bin /= 10;
            bcd += (bin % 10) << 12;
            return (UInt16)bcd;
        }

        public override string ValueString()
        {
            return Value.ToString();
        }
    }

    /// <summary>
    /// BCD两位位小数
    /// </summary>
    public class BCDF2Uint16 : HslWapper.Variable<double, UInt16>
    {
        #region 单独变量
        public BCDF2Uint16(string Area, int Addr, int? SubAddr = null) : base(Area, Addr, SubAddr)
        {
        }

        public BCDF2Uint16(string Area, int Addr, string Comment) : base(Area, Addr, null, Comment)
        {
        }

        public BCDF2Uint16(string Area, int Addr, int? SubAddr, string Comment) : base(Area, Addr, SubAddr, Comment)
        {
        }
        #endregion

        #region 属于变量组
        public BCDF2Uint16(string Area, int Addr, VariableGroupAttribute Group) : base(Area, Addr, Group)
        {
        }
        public BCDF2Uint16(string Area, int Addr, int SubAddr, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Group)
        {
        }
        public BCDF2Uint16(string Area, int Addr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, Comment, Group)
        {
        }
        public BCDF2Uint16(string Area, int Addr, int SubAddr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Comment, Group)
        {
        }
        #endregion

        /// <summary>
        /// 视在值
        /// </summary>
        public override double Value
        {
            get => BCD2Double(RawValue) / 100;
            set => RawValue = Double2BCD(value * 100);
        }

        public override double LastValue
        {
            get
            {
                return BCD2Double(LastRawValue) / 100;
            }
        }

        private double BCD2Double(UInt16 bcd)
        {
            int bin = (bcd & 0x000F);
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 100;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 1000;
            return bin;
        }

        private UInt16 Double2BCD(double x)
        {
            UInt16 bin = Convert.ToUInt16(x);
            int bcd = bin % 10;
            bin /= 10;
            bcd += (bin % 10) << 4;
            bin /= 10;
            bcd += (bin % 10) << 8;
            bin /= 10;
            bcd += (bin % 10) << 12;
            return (UInt16)bcd;
        }

        public override string ValueString()
        {
            return Value.ToString();
        }
    }

    /// <summary>
    /// 电流变量
    /// </summary>
    public class CurrentVariable : BCDF0Uint16
    {
        #region 单独变量
        public CurrentVariable(string Area, int Addr, int? SubAddr = null) : base(Area, Addr, SubAddr)
        {
        }

        public CurrentVariable(string Area, int Addr, string Comment) : base(Area, Addr, null, Comment)
        {
        }

        public CurrentVariable(string Area, int Addr, int? SubAddr, string Comment) : base(Area, Addr, SubAddr, Comment)
        {
        }
        #endregion

        #region 属于变量组
        public CurrentVariable(string Area, int Addr, VariableGroupAttribute Group) : base(Area, Addr, Group)
        {
        }
        public CurrentVariable(string Area, int Addr, int SubAddr, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Group)
        {
        }
        public CurrentVariable(string Area, int Addr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, Comment, Group)
        {
        }
        public CurrentVariable(string Area, int Addr, int SubAddr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Comment, Group)
        {
        }
        #endregion
    }

    /// <summary>
    /// 温度变量
    /// </summary>
    public class TemperatureVariable : BCDF1Uint16
    {
        #region 单独变量
        public TemperatureVariable(string Area, int Addr, int? SubAddr = null) : base(Area, Addr, SubAddr)
        {
        }

        public TemperatureVariable(string Area, int Addr, string Comment) : base(Area, Addr, null, Comment)
        {
        }

        public TemperatureVariable(string Area, int Addr, int? SubAddr, string Comment) : base(Area, Addr, SubAddr, Comment)
        {
        }
        #endregion

        #region 属于变量组
        public TemperatureVariable(string Area, int Addr, VariableGroupAttribute Group) : base(Area, Addr, Group)
        {
        }
        public TemperatureVariable(string Area, int Addr, int SubAddr, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Group)
        {
        }
        public TemperatureVariable(string Area, int Addr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, Comment, Group)
        {
        }
        public TemperatureVariable(string Area, int Addr, int SubAddr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Comment, Group)
        {
        }
        #endregion
    }

    /// <summary>
    /// BCD整数
    /// </summary>
    public class BCDF0Uint32 : HslWapper.Variable<double, UInt32>
    {
        #region 单独变量
        public BCDF0Uint32(string Area, int Addr, int? SubAddr = null) : base(Area, Addr, SubAddr)
        {
        }

        public BCDF0Uint32(string Area, int Addr, string Comment) : base(Area, Addr, null, Comment)
        {
        }

        public BCDF0Uint32(string Area, int Addr, int? SubAddr, string Comment) : base(Area, Addr, SubAddr, Comment)
        {
        }
        #endregion

        #region 属于变量组
        public BCDF0Uint32(string Area, int Addr, VariableGroupAttribute Group) : base(Area, Addr, Group)
        {
        }
        public BCDF0Uint32(string Area, int Addr, int SubAddr, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Group)
        {
        }
        public BCDF0Uint32(string Area, int Addr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, Comment, Group)
        {
        }
        public BCDF0Uint32(string Area, int Addr, int SubAddr, string Comment, VariableGroupAttribute Group) : base(Area, Addr, SubAddr, Comment, Group)
        {
        }
        #endregion

        /// <summary>
        /// 视在值
        /// </summary>
        public override double Value
        {
            get => BCD2Double(RawValue);
            set => RawValue = Double2BCD(value);
        }

        public override double LastValue
        {
            get
            {
                return BCD2Double(LastRawValue);
            }
        }

        private double BCD2Double(UInt32 bcd)
        {
            UInt32 bin = (bcd & 0x000F);
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 100;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 1000;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10000;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 100000;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 1000000;
            bcd >>= 4;
            bin += (bcd & 0x000F) * 10000000;
            return bin;
        }

        private UInt32 Double2BCD(double x)
        {
            UInt32 bin = Convert.ToUInt32(x);
            UInt32 bcd = bin % 10;
            bin /= 10;
            bcd += (bin % 10) << 4;
            bin /= 10;
            bcd += (bin % 10) << 8;
            bin /= 10;
            bcd += (bin % 10) << 12;
            bin /= 10;
            bcd += (bin % 10) << 16;
            bin /= 10;
            bcd += (bin % 10) << 20;
            bin /= 10;
            bcd += (bin % 10) << 24;
            bin /= 10;
            bcd += (bin % 10) << 28;
            return bcd;
        }

        public override string ValueString()
        {
            return Value.ToString();
        }
    }
}
